package io.github.mengfly.kvstore;

import io.github.mengfly.kvstore.util.Compressor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author wangp
 */
@RequiredArgsConstructor
public class KvEntry implements Serializable {

    private final byte crc;
    private final int keySize;
    private final int valueSize;
    private final byte[] key;
    private final byte[] value;
    /**
     * 为负数的时候表示数据已经被删除
     */
    @Getter
    private final byte mark;


    public KvEntry(String key, String value) {
        this(key, value, (byte) 1);
    }

    public KvEntry(String key, String value, byte mark) {

        // key 不进行压缩，因为在字符比较短的情况下，压缩性能反而不如不压缩
        this.key = key.getBytes(StandardCharsets.UTF_8);
        this.value = Compressor.compress(value);
        this.crc = Compressor.crc32(this.key, this.value);

        this.keySize = this.key.length;
        this.valueSize = this.value.length;

        this.mark = mark;
    }


    public void write(RandomAccessFile fos) throws IOException {
        // write crc
        fos.writeByte(crc);
        fos.writeInt(keySize);
        fos.writeInt(valueSize);
        fos.write(key);
        fos.write(value);
        fos.write(mark);
    }


    public boolean valid() {
        return this.crc == Compressor.crc32(this.key, this.value);
    }

    public String getValue() {
        if (this.valueSize == 0) {
            return null;
        }
        return Compressor.deCompress(this.value);
    }

    public String getKey() {
        return new String(this.key, StandardCharsets.UTF_8);
    }

    public static KvEntry read(RandomAccessFile raf, long position) throws IOException {
        raf.seek(position);
        return read(raf);
    }

    private static KvEntry read(RandomAccessFile raf) throws IOException {
        final byte crc = raf.readByte();

        final int keySize = raf.readInt();
        final int valueSize = raf.readInt();

        byte[] key = new byte[keySize];
        raf.read(key);

        byte[] value = new byte[valueSize];
        if (valueSize > 0) {
            raf.read(value);
        }

        final byte mark = raf.readByte();

        return new KvEntry(crc, keySize, valueSize, key, value, mark);
    }

    public static Map<String, Long> readKeyPosMap(RandomAccessFile raf) throws IOException {
        Map<String, Long> keyPosMap = new LinkedHashMap<>();
        raf.seek(0);

        final long length = raf.length();
        long curPos = raf.getFilePointer();

        while (curPos < length) {
            raf.readByte();
            final int keySize = raf.readInt();
            final int valueSize = raf.readInt();

            byte[] key = new byte[keySize];
            raf.read(key);


            raf.seek(curPos + entrySize(keySize, valueSize) - 1); // ignore mark
            final byte mark = raf.readByte();

            final String keySrc = new String(key, StandardCharsets.UTF_8);
            if (mark < 0) {
                keyPosMap.remove(keySrc);
            } else {
                keyPosMap.put(keySrc, curPos);
            }
            curPos = raf.getFilePointer();
        }
        return keyPosMap;
    }

    private static int entrySize(int keySize, int valueSize) {
        return 1 + 4 + 4 + keySize + valueSize + 1;
    }
}
